package com.tool4j.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tool4j.common.exception.BalanceException;
import com.tool4j.entity.TemplateFile;
import com.tool4j.entity.TemplateInfo;
import com.tool4j.mapper.TemplateInfoMapper;
import com.tool4j.service.TemplateFileService;
import com.tool4j.service.TemplateInfoService;
import com.tool4j.util.Md5Util;
import com.tool4j.util.UserPermission;
import com.tool4j.util.velocity.VelocityParserPlus;
import com.tool4j.util.velocity.VelocityTemplateLoader;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.exception.ParseErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;

/**
 * @author Deng.Weiping
 * @since 2023-11-24 09:02:30
 */
@Service
@Slf4j
public class TemplateInfoServiceImpl extends ServiceImpl<TemplateInfoMapper, TemplateInfo> implements TemplateInfoService {

    @Autowired
    private TemplateInfoMapper templateInfoMapper;

    @Autowired
    private TemplateFileService templateFileService;

    @Override
    public List<TemplateInfo> findAll(Long userId) {
        LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getUserId, 0L)
                .or()
                .eq(TemplateInfo::getUserId, userId)
                .orderByDesc(TemplateInfo::getCreatedDate);
        return templateInfoMapper.selectList(query);
    }

    @Override
    public List<TemplateInfo> findList(Long userId) {
        LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getUserId, userId)
                .orderByDesc(TemplateInfo::getCreatedDate);
        List<TemplateInfo> list = templateInfoMapper.selectList(query);
        for (TemplateInfo templateInfo : list) {
            List<TemplateFile> files = templateFileService.getTemplateContent(templateInfo);
            templateInfo.setFiles(files);
        }
        return list;
    }

    @Override
    public TemplateInfo findById(Long id) {
        return templateInfoMapper.selectById(id);
    }

    /**
     * 是否重复
     * <p>
     * userId + templateName + ormType 一样
     *
     * @param templateInfo
     * @return 重复-true、未重复-false
     */
    private boolean isRepeat(TemplateInfo templateInfo) {
        LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getTemplateName, templateInfo.getTemplateName())
                .eq(TemplateInfo::getUserId, templateInfo.getUserId())
                .eq(TemplateInfo::getOrmType, templateInfo.getOrmType());
        List<TemplateInfo> list = templateInfoMapper.selectList(query);
        return CollUtil.isNotEmpty(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insert(ServerWebExchange exchange, TemplateInfo templateInfo) {
        LambdaQueryWrapper<TemplateInfo> countQuery = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getUserId, templateInfo.getUserId());
        Long count = templateInfoMapper.selectCount(countQuery);
        if (count > 5) {
            throw new BalanceException("您的自定义代码模板数量已达到最大值");
        }
        templateInfo.setUserId(UserPermission.get(exchange).getUserId());
        //校验
        Assert.isTrue(StrUtil.isNotBlank(templateInfo.getTemplateName()), "请输入模板名称");
        Assert.isTrue(StrUtil.isNotBlank(templateInfo.getOrmType()), "请选择ORM框架");
        Assert.isTrue(!isRepeat(templateInfo), "模板名称已存在");

        String path = String.format("%s/%s/%s", UserPermission.get(exchange).getUserId(), templateInfo.getTemplateName(), templateInfo.getOrmType());
        templateInfo.setPath(path);
        TemplateInfo baseTemplate;
        if (templateInfo.getBaseTemplate() == null) {
            LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                    .eq(TemplateInfo::getUserId, 0L)
                    .eq(TemplateInfo::getOrmType, templateInfo.getOrmType());
            baseTemplate = templateInfoMapper.selectOne(query);
        } else {
            baseTemplate = templateInfoMapper.selectById(templateInfo.getBaseTemplate());
        }
        save(templateInfo);
        List<TemplateFile> files = templateFileService.findByTemplateId(baseTemplate.getId());
        copyDefaultFile(templateInfo.getPath(), baseTemplate.getPath(), files);
        templateFileService.saveFiles(templateInfo.getId(), files);
        return true;
    }

    @Override
    public boolean updateFile(ServerWebExchange exchange, TemplateFile file) {
        if (!file.getFileName().endsWith(".vm")) {
            file.setFileName(file.getFileName() + ".vm");
        }
        if (file.getFilePath() == null) {
            return false;
        }
        String filePath = file.getFilePath()
                .replace("\\", "/")
                .replace("\\", "/");
        if (filePath.startsWith("/")) {
            filePath = filePath.substring(1);
        }
        if (filePath.endsWith("/")) {
            filePath = filePath.substring(0, filePath.length() - 1);
        }
        file.setFilePath(filePath);
        String newMd5 = Md5Util.getMD5(file.getContent());
        file.setMd5(newMd5);
        templateFileService.updateById(file);
        TemplateInfo templateInfo = findMyTemplateById(UserPermission.get(exchange).getUserId(), file.getTemplateId());
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(file.getContent().getBytes(StandardCharsets.UTF_8))) {
            Path rootPath = Paths.get(VelocityTemplateLoader.getRootPath());
            Path templatePath = Paths.get(templateInfo.getPath());
            Path fullPath = rootPath.resolve(templatePath)
                    .resolve(Paths.get(file.getFilePath()))
                    .resolve(file.getFileName());
            FileUtil.writeFromStream(inputStream, fullPath.toString());
            String template = Paths.get(templateInfo.getPath()).resolve(file.getFilePath()).resolve(file.getFileName()).toString();
            VelocityParserPlus.verify(template);
        } catch (ParseErrorException e) {
            log.error("渲染模板失败", e);
            throw new ParseErrorException("模板渲染失败，请检查内容是否正确");
        } catch (IOException ioe) {
            log.error("操作模板文件失败", ioe);
            throw new RuntimeException(ioe.getMessage());
        }
        return true;
    }

    private TemplateInfo findMyTemplateById(Long userId, Long templateId) {
        LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getUserId, userId)
                .eq(TemplateInfo::getId, templateId);
        TemplateInfo templateInfo = templateInfoMapper.selectOne(query);
        Assert.notNull(templateInfo, "无权操作");
        return templateInfo;
    }

    private void copyDefaultFile(String rootPath, String sourceRoot, List<TemplateFile> files) {
        //上传模板文件
        for (TemplateFile file : files) {
            if (file.getLeaf()) {
                try {
                    String sourcePath = String.format("%s/%s/%s/%s", VelocityTemplateLoader.getRootPath(), sourceRoot, file.getFilePath(), file.getFileName());
                    String targetPath = String.format("%s/%s/%s", VelocityTemplateLoader.getRootPath(), rootPath, file.getFilePath());
                    FileUtil.mkdir(targetPath);
                    FileUtil.copyFile(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
                } catch (Exception e) {
                    log.error("写入模板失败", e);
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @Override
    public boolean updateFiles(ServerWebExchange exchange, List<TemplateFile> templateFiles) {
        for (TemplateFile file : templateFiles) {
            updateFile(exchange, file);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int delete(ServerWebExchange exchange, Long id) {
        TemplateInfo templateInfo = templateInfoMapper.selectById(id);
        Assert.isTrue(templateInfo != null && templateInfo.getUserId().equals(UserPermission.get(exchange).getUserId()), "删除失败");
        int row = templateInfoMapper.deleteById(id);
        boolean del = FileUtil.del(String.format("%s/%s", VelocityTemplateLoader.getRootPath(), templateInfo.getPath()));
        Assert.isTrue(del, "删除文件失败");
        return row;
    }

    @Override
    public TemplateInfo findDefault(String ormType) {
        LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getUserId, 0)
                .eq(TemplateInfo::getOrmType, ormType);
        return templateInfoMapper.selectOne(query);
    }

    private void parserKey(TemplateFile templateFile) {
        templateFile.setKey(String.format("%s/%s", templateFile.getFilePath(), templateFile.getFileName()));
    }

    @Override
    public Long saveFile(TemplateFile templateFile) {
        String path = templateFile.getFilePath()
                .replace("\\", "/")
                .replace("\\", "/");
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        if (path.endsWith("/")) {
            path = path.substring(0, path.length() - 1);
        }
        templateFile.setFilePath(path);
        TemplateInfo templateInfo = findById(templateFile.getTemplateId());
        if (templateFile.getLeaf() && !templateFile.getFileName().endsWith(".vm")) {
            templateFile.setFileName(templateFile.getFileName() + ".vm");
        }
        parserKey(templateFile);
        Assert.isTrue(!templateFileService.existsFile(templateFile.getKey(), templateFile.getTemplateId()), "文件名称重复");
        templateFileService.save(templateFile);

        // 创建模板文件
        Path rootPath = Paths.get(VelocityTemplateLoader.getRootPath());
        Path templatePath = Paths.get(templateInfo.getPath());
        Path filePath = Paths.get(templateFile.getFilePath());
        Path fullPath = rootPath.resolve(templatePath).resolve(filePath);
        Path fileFullPath = fullPath.resolve(templateFile.getFileName());
        FileUtil.touch(fileFullPath.toString());
        return templateFile.getId();
    }

    @Override
    public void delFile(Long fileId) {
        TemplateFile templateFile = templateFileService.getById(fileId);
        TemplateInfo templateInfo = findById(templateFile.getTemplateId());
        Path rootPath = Paths.get(VelocityTemplateLoader.getRootPath());
        Path templatePath = Paths.get(templateInfo.getPath());
        Path filePath = Paths.get(templateFile.getFilePath());
        Path fullPath = rootPath.resolve(templatePath).resolve(filePath);
        Path fullFilePath = fullPath.resolve(templateFile.getFileName());
        FileUtil.del(fullFilePath.toString());
        templateFileService.deleteById(fileId);
    }

    @Override
    public List<TemplateFile> getFiles(Long id) {
        TemplateInfo templateInfo = templateInfoMapper.selectById(id);
        return templateFileService.getTemplateContent(templateInfo);
    }

    @Override
    public boolean rename(ServerWebExchange exchange, TemplateInfo templateInfo) {
        LambdaQueryWrapper<TemplateInfo> query = Wrappers.lambdaQuery(TemplateInfo.class)
                .eq(TemplateInfo::getUserId, UserPermission.get(exchange).getUserId())
                .eq(TemplateInfo::getId, templateInfo.getId());
        TemplateInfo data = templateInfoMapper.selectOne(query);
        Assert.notNull(data, "模板不存在");
        data.setTemplateName(templateInfo.getTemplateName());
        templateInfoMapper.updateById(data);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean renameFile(ServerWebExchange exchange, TemplateFile templateFile) {
        TemplateInfo templateInfo = findMyTemplateById(UserPermission.get(exchange).getUserId(), templateFile.getTemplateId());
        TemplateFile oldFile = templateFileService.getById(templateFile.getId());
        //删除旧文件
        Path rootPath = Paths.get(VelocityTemplateLoader.getRootPath());
        Path templatePath = Paths.get(templateInfo.getPath());
        Path oldFilePath = Paths.get(oldFile.getFilePath());
        Path fullPath = rootPath.resolve(templatePath).resolve(oldFilePath);
        Path oldFullPath = fullPath.resolve(oldFile.getFileName());
        FileUtil.del(oldFullPath.toString());
        //保存新文件
        Path newFilePath = Paths.get(templateFile.getFilePath());
        Path newFullPath = rootPath.resolve(templatePath).resolve(newFilePath);
        Path newFullFilePath = newFullPath.resolve(templateFile.getFileName());
        FileUtil.touch(newFullFilePath.toString());
        parserKey(templateFile);
        templateFileService.updateById(templateFile);
        return true;
    }

}